<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div id="aDom"></div>
        <div id="bDom"></div>
        <script>
        //数据响应式
        function reactive(obj) {
            if (!isObject(obj)) {
                return obj
            }
            return new Proxy(obj, {
                get(target, key) {
                    //Reflect容错率不错，错误会正确的处理
                    const res = Reflect.get(target, key)
                    console.log('get', res)
                    track(target, key)
                    return isObject(res) ? reactive(res) : res
                },
                set(target, key, value) {
                    const res = Reflect.set(target, key, value)
                    console.log('set', key)
                    trigger(target, key)
                    return res
                },
                deleteProperty(target, key) {
                    const res = Reflect.deleteProperty(target, key)
                    console.log('del', key)
                    trigger(target, key)
                    return res
                }
            })
        }
        function isObject(obj) {
            return typeof obj === 'object'
        }
        // 保存函数
        const effectStack = []
        //保存映射关系的数据结构
        const targetMap = new WeakMap()
        //触发依赖收集
        function effect(fn) {
            //对照源码简化的高阶函数，可以从简，但是为了原滋原味
            const e = createReactiveEffect(fn)
            //自运行
            e()
            return e
        }
        function createReactiveEffect(fn) {
            //错误处理
            //effectStack 进栈
            //执行完依赖收集后出栈保证effectStack最后值的获取
            const effectFn = function () {
                try {
                    effectStack.push(effectFn)
                    fn()
                } finally {
                    effectStack.pop()
                }
            }
            return effectFn
        }
        //跟踪函数：负责依赖收集
        function track(target, key) {
            //1. 获取effectFn
            const effect = effectStack[effectStack.length - 1]
            if (effect) {
                //用过target获取对应的map  每一层级的数据对应一个targetMap   target:Map
                let depMap = targetMap.get(target)
                //首次进入depMap为空,需要创建
                if (!depMap) {
                    depMap = new Map()
                    targetMap.set(target, depMap)
                }
                // 2. 通过key获取依赖集合set
                let deps = depMap.get(key)
                if (!deps) {
                    deps = new Set()
                    depMap.set(key, deps)
                }
                // 3. 放入effect
                deps.add(effect)
                //层级关系是 obj:{key:effect}  三层套娃   WeakMap ==> Map ==>Set
            }
        }
        //触发函数：track()相反操作，拿出映射关系，执行所有的cbs
        function trigger(target, key) {
            const depMap = targetMap.get(target)
            if (!depMap) return
            const deps = depMap.get(key)
            if (deps) {
                deps.forEach(dep => dep())
            }
        }
        const obj = reactive({ a: 'a', b: { c: 1 } })
        //源码中  这里做虚拟dom的path，这里简单的用textContent改变页面上的值做响应式
        effect(() => {
            aDom.textContent = obj.a
            console.log('effect1', obj.a)
        })
        //深层次的副作用
        effect(() => {
            bDom.textContent = obj.b.c
            console.log('effect2', obj.b.c)
        })
        setTimeout(() => {
            obj.a = '777'
            //当改变深度子节点 只会触发一次trigger 因为weakMap是以obj的target(递归中就是每一层的对象)存储的
            obj.b.c = "666"
        }, 2000)
    </script>
    </body>
</html>